﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;

using System.Windows.Forms;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Microsoft.Office.Interop.Outlook;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.Net;

using MySCM.Agent.Xml;
using MySCM.Agent.Log;
using Microsoft.Win32;
using System.IO;

namespace MySCM.Utility.OutlookAddIn
{
    public partial class ProductBacklog
    {
        // === Outlook variables
        const string CreateWorkItem = "Create Product Backlog";
        const string UpdateWorkItem = "Update Product Backlog";
        const string RefreshMailItem = "Refresh MailItem";
        Microsoft.Office.Interop.Outlook.Explorer _explorer = null;

        // === TFS variables
        static NetworkCredential tfsCredential;
        static Uri tfsUri;
        static string tfsProjectName = "";
        static string tfsWorkItemType = "";
        static string outlookUserProperty = "";

        //static string appRoot = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location + "..");
        static string appRoot = Path.GetDirectoryName((string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Office\Outlook\Addins\MySCM", "Manifest", null)).ToString();
        static LogAgent logAgent = new LogAgent(appRoot + @"\Logs\");
        static ConfigXmlAgent configAgent = new ConfigXmlAgent(appRoot + @"\Config\Config.xml");


        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
            MessageBox.Show("appRoot: " + appRoot);

            // === Read Variables from the config file
            ReadFromConfigFile();

            // === cache the explorer object
            _explorer = this.Application.ActiveExplorer();

            // === when an email selection changes this event will fire
            _explorer.SelectionChange += new ExplorerEvents_10_SelectionChangeEventHandler(_explorer_SelectionChange);
        }


        public void ReadFromConfigFile()
        {
            string debugFlag = "";
            // Get TFS info
            tfsCredential = new NetworkCredential(configAgent.GetNodeValue("LoginUser"), configAgent.GetNodeValue("LoginPassword"), configAgent.GetNodeValue("LoginDomain"));            
            tfsProjectName = configAgent.GetNodeValue("ProjectName");
            tfsUri = new Uri(configAgent.GetNodeValue("Uri") + configAgent.GetNodeValue("CollectionName"));
            tfsWorkItemType = configAgent.GetNodeValue("WorkItemType");

            // Get Outlook info
            outlookUserProperty = configAgent.GetNodeValue("UserPropertyName");

            // Debug
            if (debugFlag == "1")
            {
                logAgent.WriteLog("Design", "Info", "LoginUser:" + configAgent.GetNodeValue("LoginUser"));
                logAgent.WriteLog("Design", "Info", "LoginPassword:" + configAgent.GetNodeValue("LoginPassword"));
                logAgent.WriteLog("Design", "Info", "LoginDomain:" + configAgent.GetNodeValue("LoginDomain"));
                logAgent.WriteLog("Design", "Info", "CollectionName:" + configAgent.GetNodeValue("CollectionName"));
                logAgent.WriteLog("Design", "Info", "ProjectName:" + configAgent.GetNodeValue("ProjectName"));
                logAgent.WriteLog("Design", "Info", "WorkItemType:" + configAgent.GetNodeValue("WorkItemType"));
                logAgent.WriteLog("Design", "Info", "Uri:" + configAgent.GetNodeValue("Uri"));
            }
        }


        // event fired when any selection changes.
        void _explorer_SelectionChange()
        {
            //MessageBox.Show("_explorer_SelectionChange() - _explorer.Selection.Count:" + _explorer.Selection.Count);
            foreach (object selectedItem in _explorer.Selection)
            {
                // we only want to deal with selected mail items
                MailItem mailItem = selectedItem as MailItem;
                if (mailItem != null)
                {
                    //MessageBox.Show("selectedItem:" + selectedItem.ToString() + "\n" + "mailItem:" + mailItem.Subject);
                    // Remove all existing actions
                    DeleteExistingCustomActions(mailItem);
                    AddNewCustomActions(mailItem);
                }
            }
        }


        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }


        public void DeleteExistingCustomActions(MailItem mailItem)
        {
            string[] existingCustomActions = { "Create Task", "Update Task", "Refresh Task", "Refresh Product Backlog", CreateWorkItem, UpdateWorkItem, RefreshMailItem };
            foreach (string customAction in existingCustomActions)
            {
                if (mailItem.Actions[customAction] != null)
                    mailItem.Actions[customAction].Delete();
            }
        }


        public void AddNewCustomActions(MailItem mailItem)
        {
            string[] newCustomActions = { CreateWorkItem, UpdateWorkItem, RefreshMailItem };
            foreach (string customAction in newCustomActions)
            {
                if (mailItem.Actions[customAction] == null)
                {
                    Microsoft.Office.Interop.Outlook.Action newAction;
                    newAction = mailItem.Actions.Add();
                    newAction.Name = customAction;
                    newAction.ShowOn = OlActionShowOn.olMenu;
                    newAction.Enabled = true;
                    mailItem.Save();
                }
            }

            // add the event handler for our action
            mailItem.CustomAction += new ItemEvents_10_CustomActionEventHandler(item_CustomAction);
        }


        void item_CustomAction(object Action, object Response, ref bool Cancel)
        {
            try
            {
                Microsoft.Office.Interop.Outlook.Action mailAction = (Microsoft.Office.Interop.Outlook.Action)Action;
                MailItem mailItem = _explorer.Selection[1] as MailItem;
                if (mailItem != null)
                {
                    switch (mailAction.Name)
                    {
                        // only process the action we know about
                        case CreateWorkItem:
                            CreateTfsWorkItem(mailItem);
                            break;
                        case UpdateWorkItem:
                            UpdateTfsWorkItem(mailItem);
                            break;
                        case RefreshMailItem:
                            RefreshOutlookMailItem(mailItem);
                            break;
                    }
                }
            }
            finally
            {
                Cancel = true;
            }
        }


        // ======================================================================================
        // = Create TFS WorkItem
        // =    * Set the mail item sender as the original Assign To
        // =    * Change the Assign To to me/current user once the work item been created
        // ======================================================================================
        public void CreateTfsWorkItem(MailItem mailItem)
        {
            //MessageBox.Show("Enter UpdateTfsWorkItem - MailItem.Date:***" + mailItem.SentOn.ToLongTimeString());
            if (mailItem != null)
            {
                // === Connect to TFS collection
                TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(tfsUri, tfsCredential);
                WorkItemStore store = tpc.GetService<WorkItemStore>();
                Project tfsProject = store.Projects[tfsProjectName];
                WorkItemType wiType = tfsProject.WorkItemTypes[tfsWorkItemType];

                // === Create a new workitem
                WorkItem workItem = new WorkItem(wiType);
                workItem.Title = mailItem.Subject;
                string descriptionHtml = "";
                descriptionHtml = descriptionHtml + "Subject: " + mailItem.Subject + "<br>";
                descriptionHtml = descriptionHtml + "From   : " + mailItem.Sender.Name + "<br>";
                descriptionHtml = descriptionHtml + "To/CC  : ";
                foreach (Recipient recipient in mailItem.Recipients)
                {
                    descriptionHtml = descriptionHtml + recipient.Name + "; ";
                }
                workItem["Description Html"] = descriptionHtml + "<br><br>" + mailItem.Body;
                workItem["Assigned To"] = mailItem.Sender.Name; // Set the Assign to to the mail sender
                string resultString = "";
                var validateResults = workItem.Validate();
                foreach (var validateResult in validateResults)
                    resultString = resultString + validateResult.ToString() + "\n";
                //MessageBox.Show("Validate Result:\n" + resultString);
                workItem.Save();

                // === Change Assign To to me/current user
                WorkItemCollection workItems = store.Query("Select * from WorkItems Where [System.Id] = " + workItem.Id);
                foreach (WorkItem workitem in workItems)
                {
                    workitem["Assigned To"] = _explorer.Session.CurrentUser.Name;
                    workitem.Save();
                }

                // === Add a custom field to OutLook mail item showing WorkItem information
                if (mailItem.UserProperties.Find("WorkItem") == null)
                {
                    mailItem.UserProperties.Add("WorkItem", OlUserPropertyType.olText, true, OlFormatText.olFormatTextText).Value = workItem.Id + "-" + workItem.State;
                }

                MessageBox.Show(string.Format("Task {0} has been created successfully.", workItem.Id));
            }
            else
            {
                MessageBox.Show("Unable to convert selected item to a mail item");
            }
        }


        // ======================================================================================
        // = Update TFS WorkItem
        // =    * Create a new workitem if it is a new conversation (tracked within TFS)
        // =    * Update TFS work item based on existing ID from either itself or conversation
        // =    * Update Outlook mail item based on TFS workitem state
        // ======================================================================================
        public void UpdateTfsWorkItem(MailItem mailItem)
        {
            // MessageBox.Show("Enter UpdateTfsWorkItem");
            string workItem = "";
            string workItemID = "";
            string workItemState = "";
            try
            {
                if (mailItem.UserProperties.Find("WorkItem") == null && GetMailItemPropertyFromConversation(mailItem, "WorkItem") == "")
                {
                    // === Brand new conversation -> Create a new workitem
                    CreateTfsWorkItem(mailItem);
                }
                else
                {
                    if (mailItem.UserProperties.Find("WorkItem") != null)
                        // === Get WorkItem value of its own
                        workItem = mailItem.UserProperties.Find("WorkItem").Value;
                    else
                        // === Get WorkItem value from its conversation
                        workItem = GetMailItemPropertyFromConversation(mailItem, "WorkItem");

                    if (workItem != "")
                    {
                        // === Parse the WorkItem value so we can update the workitem based on the ID and refresh its state back to MailItem
                        workItemID = workItem.Split('-')[0];
                        workItemState = workItem.Split('-')[1];
                        //MessageBox.Show("workItemID:***" + workItemID + "***");

                        TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(tfsUri, tfsCredential);
                        WorkItemStore store = tpc.GetService<WorkItemStore>();
                        Project tfsProject = store.Projects[tfsProjectName];
                        WorkItemType wiType = tfsProject.WorkItemTypes[tfsWorkItemType];
                        WorkItemCollection workItems = store.Query("Select * from WorkItems Where [System.ID] = '" + workItemID + "'");
                        if (workItems.Count > 1) // Unable to identify the right workitem
                        {
                            MessageBox.Show(string.Format("Could not find the right workitem based on the email subject. More than one workitems found for similar subject ({0})", mailItem.Subject));
                        }
                        else if (workItems.Count == 0)
                        {
                            MessageBox.Show(string.Format("WorkItem ({0}) Not Found.", mailItem.Subject));
                        }
                        else // Update workitem
                        {
                            WorkItem workitem = workItems[0];
                            workitem["Assigned To"] = _explorer.Session.CurrentUser.Name;
                            workitem["History"] = mailItem.Body;
                            workitem.Save();
                            MessageBox.Show(string.Format("Task {0} has been updated successfully.", workitem.Id));

                            // === Add a custom field to OutLook mail item showing WorkItem information
                            UserProperty upWorkItem = mailItem.UserProperties.Find("WorkItem");
                            if (upWorkItem == null)
                            {
                                mailItem.UserProperties.Add("WorkItem", OlUserPropertyType.olText, true, OlFormatText.olFormatTextText).Value = workitem.Id + "-" + workitem.State;
                            }
                            else
                            {
                                upWorkItem.Value = workitem.Id + "-" + workitem.State;
                                mailItem.Save();
                            }
                        }
                    }
                }
            }
            catch (System.Exception e)
            {
                logAgent.WriteLog("Design", "Exception", e.Message);
                throw e;
            }
        }


        // ==================================================
        // = Refresh Outlook MailItem
        // ==================================================
        public void RefreshOutlookMailItem(MailItem mailItem)
        {
            try
            {
                if (mailItem != null)
                {
                    // === Get WorkItem information
                    string workItem = "";
                    string workItemID = "";
                    string workItemState = "";
                    UserProperty upWorkItem = mailItem.UserProperties.Find("WorkItem");
                    if (upWorkItem != null)
                    {
                        workItem = mailItem.UserProperties.Find("WorkItem").Value;
                        workItemID = workItem.Split('-')[0];
                        workItemState = workItem.Split('-')[1];
                    }

                    // === Connect to TFS
                    TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(tfsUri, tfsCredential);
                    WorkItemStore store = tpc.GetService<WorkItemStore>();
                    Project tfsProject = store.Projects[tfsProjectName];
                    WorkItemType wiType = tfsProject.WorkItemTypes[tfsWorkItemType];
                    //WorkItem workItem = new WorkItem(wiType);

                    WorkItemCollection workItems = store.Query("Select * from WorkItems Where [System.ID] = '" + workItemID + "'");
                    if (workItems.Count > 1) // Unable to identify the right workitem
                    {
                        MessageBox.Show(string.Format("Could not find the right workitem based on the email subject. More than one workitems found for similar subject ({0})", mailItem.Subject));
                    }
                    else if (workItems.Count == 0)
                    {
                        MessageBox.Show(string.Format("Task ({0}) Not Found.", mailItem.Subject));
                    }
                    else // Refresh the workitem info and update the email user property
                    {
                        if (upWorkItem != null)
                        {
                            WorkItem workitem = workItems[0];
                            upWorkItem.Value = workItemID + "-" + workitem.State;
                            mailItem.Save();
                            MessageBox.Show(string.Format("Email has been refreshed successfully"));
                        }
                    }
                }
                else
                {
                    MessageBox.Show("Unable to convert selected item to a mail item");
                }
            }
            catch (System.Exception e)
            {
                throw e;
            }
        }


        public string GetMailItemPropertyFromConversation(MailItem mailItem, string propertyName)
        {
            string propertyValue = "";
            Outlook.Folder folder = mailItem.Parent as Outlook.Folder;
            Outlook.Store store = folder.Store;
            if (store.IsConversationEnabled == true)
            {
                // Obtain a Conversation object. 
                Outlook.Conversation conv = mailItem.GetConversation();

                // Check for null Conversation. 
                if (conv != null)
                {
                    // Obtain Table that contains rows 
                    // for each item in the conversation. 
                    Outlook.Table table = conv.GetTable();
                    //MessageBox.Show("Conversation Items Count: " + table.GetRowCount().ToString());
                    logAgent.WriteLog("Design", "Info", "Conversation Items Count: " + table.GetRowCount().ToString());
                    //MessageBox.Show("Conversation Items from Table:");
                    //////while (!table.EndOfTable)
                    //////{
                    //////    Outlook.Row nextRow = table.GetNextRow();
                    //////    MessageBox.Show(nextRow["Subject"] + " Modified: " + nextRow["LastModificationTime"]);
                    //////}
                    //MessageBox.Show("Conversation Items from Root:");
                    // Obtain root items and enumerate the conversation. 
                    Outlook.SimpleItems simpleItems = conv.GetRootItems();
                    //Outlook.SimpleItems simpleItems = conv.GetChildren(mailItem);
                    int i = 0;
                    foreach (object item in simpleItems)
                    {
                        i++;
                        // MessageBox.Show(i.ToString());

                        // In this example, only enumerate MailItem type. 
                        // Other types such as PostItem or MeetingItem 
                        // can appear in the conversation. 
                        if (item is Outlook.MailItem)
                        {
                            Outlook.MailItem mail = item as Outlook.MailItem;
                            Outlook.Folder inFolder = mail.Parent as Outlook.Folder;
                            string msg = "***RootItem***\nSubject: " + mail.Subject + "\nFolder: " + inFolder.Name + "\nReceived Time: " + mail.ReceivedTime + "\ni: " + i;
                            //MessageBox.Show(msg);
                            // Call EnumerateConversation 
                            // to access child nodes of root items.                             
                            propertyValue = EnumerateConversation(mail, conv, propertyName);
                            //propertyValue = GetMailItemProperty(mailItem, propertyName);
                            if (propertyValue != "")
                            {
                                // MessageBox.Show("propertyValue not eaquals empty" + mail.Subject + " in folder " + inFolder.Name + " - " + mail.ReceivedTime);
                                break;
                            }
                        }
                    }
                }
            }
            return propertyValue;
        }


        public string EnumerateConversation(object item, Outlook.Conversation conversation, string propertyName)
        {
            string propertyValue = "";
            propertyValue = GetMailItemProperty((MailItem)item, propertyName);
            if (propertyValue != "")
                return propertyValue;
            else
            {
                Outlook.SimpleItems items = conversation.GetChildren(item);
                //if (items.Count > 0)
                //{
                foreach (object myItem in items)
                {
                    // In this example, only enumerate MailItem type. 
                    // Other types such as PostItem or MeetingItem 
                    // can appear in the conversation. 
                    if (myItem is Outlook.MailItem)
                    {
                        Outlook.MailItem mailItem = myItem as Outlook.MailItem;
                        Outlook.Folder inFolder = mailItem.Parent as Outlook.Folder;
                        propertyValue = GetMailItemProperty(mailItem, propertyName);
                        string msg = "Folder:***" + inFolder.Name + "***\n"
                                   + "Subject:***" + mailItem.Subject + "***\n"
                                   + "ConversationID:***" + conversation.ConversationID + "***\n"
                                   + "WorkItem:***" + propertyValue + "***\n";
                        //MessageBox.Show(msg);
                        if (propertyValue == "")
                        {
                            // Continue recursion. 
                            propertyValue = EnumerateConversation(myItem, conversation, propertyName);
                            if (propertyValue != "")
                                break;
                        }
                        else
                        {
                            // MessageBox.Show(mailItem.ReceivedTime + " - userProperty:***" + propertyValue + "***");
                            break;
                        }
                    }
                }
            }
            //}
            //else
            //    MessageBox.Show("Children Count <= 0");
            return propertyValue;
        }


        public string GetMailItemProperty(MailItem mailItem, string propertyName)
        {
            string propertyValue = "";
            UserProperty upWorkItem = mailItem.UserProperties.Find(propertyName);
            if (upWorkItem != null) // Found the property of the mail item
            {
                propertyValue = upWorkItem.Value;
            }
            return propertyValue;
        }


        public string GetMailItemProperty(MailItem mailItem, string propertyName, string teamProject, string workItemType)
        {
            string propertyValue = "";

            // === Get WorkItem information
            string workItem = "";
            string workItemID = "";
            string workItemState = "";
            UserProperty upWorkItem = mailItem.UserProperties.Find(propertyName);
            if (upWorkItem != null) // Found the property of the mail item
            {
                propertyValue = upWorkItem.Value;
            }
            else // 
            {
                // workItem = mailItem.UserProperties.Find(propertyName).Value;
                workItem = upWorkItem.Value;
                workItemID = workItem.Split('-')[0];
                workItemState = workItem.Split('-')[1];

                TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(tfsUri, tfsCredential);

                WorkItemStore store = tpc.GetService<WorkItemStore>();
                Project tfsProject = store.Projects[teamProject];
                WorkItemType wiType = tfsProject.WorkItemTypes[workItemType];
                //WorkItem workItem = new WorkItem(wiType);

                WorkItemCollection workItems = store.Query("Select * from WorkItems Where [System.ID] = '" + workItemID + "'");
                if (workItems.Count > 1) // Unable to identify the right workitem
                {
                    //MessageBox.Show(string.Format("Could not find the right workitem based on the email subject. More than one workitems found for similar subject ({0})", mailItem.Subject));
                }
                else if (workItems.Count == 0)
                {
                    //MessageBox.Show(string.Format("Task ({0}) Not Found.", mailItem.Subject));
                }
                else // Refresh the workitem info and update the email user property
                {
                    if (upWorkItem != null)
                    {
                        WorkItem workitem = workItems[0];
                        upWorkItem.Value = workItemID + "-" + workitem.State;
                        //MessageBox.Show(string.Format("Email has been refreshed successfully...Re-focus the selected email to show the refreshed info"));
                    }
                }
            }
            return propertyValue;
        }


        public void RefreshMailItemProperty(MailItem mailItem, string propertyName, string teamProject, string workItemType)
        {
            string propertyValue = "";

            // === Get WorkItem information
            string workItem = "";
            string workItemID = "";
            string workItemState = "";
            UserProperty upWorkItem = mailItem.UserProperties.Find(propertyName);
            if (upWorkItem != null) // Found the property of the mail item
            {
                propertyValue = upWorkItem.Value;
            }
            else // 
            {
                // workItem = mailItem.UserProperties.Find(propertyName).Value;
                workItem = upWorkItem.Value;
                workItemID = workItem.Split('-')[0];
                workItemState = workItem.Split('-')[1];

                TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(tfsUri, tfsCredential);

                WorkItemStore store = tpc.GetService<WorkItemStore>();
                Project tfsProject = store.Projects[teamProject];
                WorkItemType wiType = tfsProject.WorkItemTypes[workItemType];
                //WorkItem workItem = new WorkItem(wiType);

                WorkItemCollection workItems = store.Query("Select * from WorkItems Where [System.ID] = '" + workItemID + "'");
                if (workItems.Count > 1) // Unable to identify the right workitem
                {
                    //MessageBox.Show(string.Format("Could not find the right workitem based on the email subject. More than one workitems found for similar subject ({0})", mailItem.Subject));
                }
                else if (workItems.Count == 0)
                {
                    //MessageBox.Show(string.Format("Task ({0}) Not Found.", mailItem.Subject));
                }
                else // Refresh the workitem info and update the email user property
                {
                    if (upWorkItem != null)
                    {
                        WorkItem workitem = workItems[0];
                        upWorkItem.Value = workItemID + "-" + workitem.State;
                        //MessageBox.Show(string.Format("Email has been refreshed successfully...Re-focus the selected email to show the refreshed info"));
                    }
                }
            }
        }


        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion
    }


}


